/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package edu.gu.derp.database;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.NoResultException;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Root;

/**
 *
 * @author Alexander & Christoffer
 */
public class Queries {

    private final EntityManagerFactory emf;
    private EntityManager em;
    private EntityTransaction tx;

    public Queries(EntityManagerFactory emf) {
        this.emf = emf;
        em = emf.createEntityManager();
    }

    // Account queries
    public boolean addUser(Account user) {
        if (getUser(user.getUsername()) != null) {
            return false;
        } else {
            try {
                tx = em.getTransaction();
                tx.begin();
                em.persist(user);
                tx.commit();
                return true;
            } catch (RuntimeException e) {
                if (tx != null && tx.isActive()) {
                    tx.rollback();
                }
                throw e; // or display error message
            }
        }
    }

    public Account getUser(String username) {

        CriteriaBuilder builder = emf.getCriteriaBuilder();
        CriteriaQuery<Account> criteria = builder.createQuery(Account.class);
        Root<Account> userRoot = criteria.from(Account.class);
        criteria.select(userRoot);
        criteria.where(builder.equal(userRoot.get(Account_.username), username));
        try {
            Account user = (Account) em.createQuery(criteria).getSingleResult();
            return user;
        } catch (NoResultException e) {
            return null;
        }
    }

    public boolean removeUser(String username) {
        Account userToRemove = getUser(username);
        if (userToRemove == null) {
            return false;
        } else {
            try {
                removeUserFromFeeds(userToRemove);
                tx = em.getTransaction();
                tx.begin();
                em.remove(userToRemove);
                tx.commit();
            } catch (RuntimeException e) {
                if (tx != null && tx.isActive()) {
                    tx.rollback();
                }
                throw e;
            }
            return true;
        }
    }
    
    // Help method to remove a specific user from every feeds' list over users
    private void removeUserFromFeeds(Account user) {
        List<Feed> feeds = user.getFeeds();
        Iterator<Feed> it = feeds.iterator();
        Feed feed = new Feed();
        while(it.hasNext()) {
            feed = it.next();
            feed.removeUser(user);
            updateFeed(feed.getUrl(), feed.getUsers());
            if(feed.getUsers().isEmpty()) {
                removeFeed(feed.getUrl());
            }
        }
    }

    public boolean changePassword(String username, String newPass) {
        try {
            Account user = getUser(username);
            user.setPassword(newPass);
            tx = em.getTransaction();
            tx.begin();
            em.persist(user);
            tx.commit();
        } catch (RuntimeException e) {
            if (tx != null && tx.isActive()) {
                tx.rollback();
                return false;
            }
            throw e; // or display error message
        }
        return true;
    }

    public boolean changeEmail(String username, String newMail) {
        try {
            Account user = getUser(username);
            user.setEmail(newMail);
            tx = em.getTransaction();
            tx.begin();
            em.persist(user);
            tx.commit();
        } catch (RuntimeException e) {
            if (tx != null && tx.isActive()) {
                tx.rollback();
                return false;
            }
            throw e; // or display error message
        }
        return true;
    }

    // Feed queries
    public boolean addFeed(Feed feed) {
        try {
            tx = em.getTransaction();
            tx.begin();
            em.persist(feed);
            tx.commit();
            return true;
        } catch (RuntimeException e) {
            if (tx != null && tx.isActive()) {
                tx.rollback();
            }
            throw e; // or display error message
        }
    }

    public boolean removeFeed(String url) {
        Feed feedToRemove = getFeed(url);
        try {
            tx = em.getTransaction();
            tx.begin();
            em.remove(feedToRemove);
            tx.commit();
        } catch (RuntimeException e) {
            if (tx != null && tx.isActive()) {
                tx.rollback();
            }
            throw e;
        }
        return true;
    }

    public Feed getFeed(String url) {
        CriteriaBuilder builder = emf.getCriteriaBuilder();
        CriteriaQuery<Feed> criteria = builder.createQuery(Feed.class);
        Root<Feed> userRoot = criteria.from(Feed.class);

        criteria.select(userRoot);

        criteria.where(builder.equal(userRoot.get(Feed_.feedURL), url));

        try {
            Feed feed = (Feed) em.createQuery(criteria).getSingleResult();
            return feed;
        } catch (NoResultException e) {
            return null;
        }
    }
 
    public boolean updateFeed(String feedUrl, List<Account> userList) {
        try {
            Feed feed = getFeed(feedUrl);
            feed.setUsers(userList);
            tx = em.getTransaction();
            tx.begin();
            em.persist(feed);
            tx.commit();
        } catch (RuntimeException e) {
            if (tx != null && tx.isActive()) {
                tx.rollback();
                return false;
            }
            throw e; // or display error message
        }
        return true;
    }
   
    public List<Feed> getUserFeeds(String user) {
        return new ArrayList<Feed>(getUser(user).getFeeds());
    }
    
    // Method putting the user in the feed's list over user and 
    // the feed in the user's list over his/her feeds
    public boolean linkUserFeed(Account user, String url) {
        Feed feed = getFeed(url);
        if(feed == null) {
            feed = new Feed();
            feed.setUrl(url);
            feed.setUsers(new ArrayList<Account>());
            feed.addUser(user);
            user.addFeed(feed);
            addFeed(feed);
        }
        else {
            feed.addUser(user);
            user.addFeed(feed);
            updateFeed(url, feed.getUsers());
        }
        return true;
    }
   
    public boolean unlinkUserFeed(Account user, String url) {
        Feed feed = getFeed(url);
        
        feed.removeUser(user);
        user.removeFeed(feed);
        updateFeed(url, feed.getUsers());
        
        return true;
    }
}